/*
 * @Description: unit2
 * @version: 0.0.1
 * @Author: 小妖怪
 * @Date: 2021-07-25 11:13:01
 * @LastEditors: Mr.小妖怪
 * @LastEditTime: 2022-02-20 09:48:02
 * @FilePath: \study_typescript\demo\part2\index.ts
 */
// 面向对象 通过对象去操作 通过 class 关键字定义对象
{
  class Person {

    /**
     * 直接定义的属性是实例属性，需要 new 关键字实例化对象才能访问，
     * const per = new Person()
     * per.name
     * 使用static 开头的属性是静态属性（类属性），直接通过类访问
     * Person.age
     * 在使用 readonly 关键字 后属性为只读属性，可读不可写
     * per.sex = 'female' // 错误 只读属性
     */
    name: string = 'justin'
    static age: number = 33
    readonly sex: string = 'male'

    // 方法
    sayHello() {
      // 方法体
      console.log('hello:', this.name)
    }
    static sayHi() {
      console.log(`sayHi ${this.name}, 我是静态方法`);
    }

  }
  let person = new Person

  console.log(person.name, Person.age);
  person.sayHello()
  Person.sayHi()
}

{
  class Dog {
    name: string;
    age: number;
    // 构造函数，会在对象创建的时候调用
    constructor(name: string, age: number) {
      // this 表当前实例
      this.name = name;
      this.age = age
    }
    say() {
      console.log(`my name is ${this.name} `)
    }
  }
  let dog = new Dog('justin', 22)
  dog.say()
}

{
  // 继承

  class Animal {
    name: string;
    age: number;
    constructor(name: string, age: number) {
      this.name = name;
      this.age = age
    }
    sayHello() {
      console.log(`${this.name} 在说话~~`)
    }
  }

  /**
   *  Dog 继承 Animal 类
   * 子类将拥有父类所有方法
   * 子类有父类的方法叫重写
  */
  class Dog extends Animal {
    sayHello() {
      super.sayHello()
    }
  }

  class Cat extends Animal {
    sayHello() {
      console.log(`${this.name} 喵喵喵`)
    }
  }

  class Snake extends Animal {
    // 当子类出现 constructor 时，需要调用 super() 父类
    constructor(name: string, age: number) {
      super(name, age);
    }
  }

  let dog = new Dog('well', 33)
  let cat = new Cat('lucy', 3)
  dog.sayHello()
  cat.sayHello()
}

{
  /**
   * 抽象类 只能被继承，不能被实例化
   *  抽象类中可以有抽象方法
   */
  abstract class Animate {
    name: string;
    age: number;
    abstract sayHello(): void
  }

  class Dog extends Animate {
    sayHello(): void {
      console.log('哈哈');
    }
  }

  let dog = new Dog()
  dog.sayHello()

}

{
  // 接口

  /**
   * 接口用来定义一个类结构
   * 用来定义一个类中应该包含哪些属性
   * 同时接口也可以当成类型声明去使用
   */

  interface myInterface {
    name: string;
    age: number;
  }

  interface myInterface {
    gender: string;
  }
  let obj: myInterface = {
    name: 'justin',
    age: 3,
    gender: 'male'
  }
  console.log(obj)

  /**
   * 接口可以定义类的时候去限制类的结构
   * 接口中的所有属性都不能有实际的值
   * 接口只定义对象的结构，不考虑实际值
   *  在接口中所有的方法都是抽象方法
   */

  interface myInter {
    name: string;
    sayHello(): void
  }

  /**
   * 定义类时，可以使类实现一个接口
   *    实现接口就是使类满足接口的要求
   */
  class MyClass implements myInter {
    name: string;
    constructor(name: string) {
      this.name = name
    }
    sayHello(): void {
      console.log(`my name is ${this.name}`);
    }
  }
  let mm = new MyClass('Lucy')
  mm.sayHello()
}

{
  // 属性的封装

  class Person {
    /**
     * public 修饰符属性可以任意的访问
     * private 修饰符只能在类的内部进行访问
     *    - 通过类中添加方法使私有属性可以被外部访问
     * protected 受保护的属性，只能在当前类和当前类的子类访问
     */
    // name: string;
    // age: number;
    private _name: string;
    private _age: number
    constructor(name: string, age: number) {
      // this.name = name
      this._name = name;
      // this.age = age
      this._age = age
    }

    // 通过 get set 来控制属性的读取
    // getName() {
    //   return this._name
    // }
    // setName(name: string) {
    //   this._name = name
    // }
    // getAge() {
    //   return this._age
    // }
    // setAge(age: number) {
    //   this._age = age
    // }
    // 也可以直接通过 get 关键字 来定义get set 属性

    get name() {
      return this._name
    }
    set name(val: string) {
      this._name = val
    }
    get age() {
      return this._age
    }
    set age(val: number) {
      this._age = val
    }

  }

  let per = new Person('justin', 23)
  // per._name = '23'

  console.log(per.name, per.age);
}

{
  class A {

    // private 关键字不能被继承，不能在类的外部访问
    private name: string;
    age: number;
    // protected 受保护的，同private 类似，不过可以被继承
    protected sex: string;
    constructor(name: string) {
      this.name = name
      this.sex = 'male'
    }
  }

  class B extends A {
    test() {
      console.log(this.age)
      console.log(this.sex);
    }
  }
  let b = new B('辛双清')
  b.test()
}

{
  // 泛型
  /**
   * 在定义函数或类时，如果遇到类型不明确时就可以使用泛型
   */
  function fn<T>(a: T): T {
    return a
  }
  // 直接调用 不指定泛型
  let result1 = fn(30)
  // 指定泛型
  console.log(fn<string>('韩宝驹'))

  function fn2<T, K>(a: T, b: K): T {
    console.log(a, b);
    return a
  }

  fn2<number, string>(1233, '英白罗')

  interface Inter {
    length: number
  }

  // T extends Inter 表示泛型 T 必须是 Inter 实现类
  function fn3<T extends Inter>(a: T): number {
    return a.length
  }

  let t: Inter = { length: 3 }
  fn3(t)

  class MyClass<T>{
    constructor(public name: T) { }
  }
  const mc = new MyClass<string>('王元霸')
  console.log(mc.name);
}